#include "LecteurPhraseAvecArbre.h"
#include "LecteurSymbole.h"
#include "Symbole.h"
#include "ListeException.h"
#include <stdlib.h>
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////

LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
ls(nomFich), ts() {
    this->juste = true;
}


////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::analyse() {

    try {
        arbre = programme();
        if (juste == true) {
        cout << "Syntaxe Correcte" << endl << endl;
        }
        else {
            cout << "Syntaxe Incorrecte" << endl << endl;
        throw ErreurSymbole();
        }
        
    }    catch (FinFichier) {
        
        throw;    
    }
}
////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::traductionCpplus() {
    cout << " #include <stdlib.h> \n #include <iostream> \n using namespace std; \n main() {\n";
    ts.tradC(1);
    arbre->tradC(1);
    cout << "\n }";
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::programme() {
    // <programme> ::= debut <seq_inst> fin FIN_FICHIER
    try {
        sauterSymCour("debut");
        Noeud* si = seqInst();
        sauterSymCour("fin");
        testerSymCour("<FINDEFICHIER>");
        return si;
 
    } catch (exception) {
        throw;
    }


}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::seqInst() {
    // <seqInst> ::= <inst> ; { <inst> ; }
    try {
    NoeudSeqInst* si = new NoeudSeqInst();

        do {
            si->ajouteInstruction(inst());
            sauterSymCour(";");
        } while (ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "si" || ls.getSymCour() == "tantque"
                || ls.getSymCour() == "repeter" || ls.getSymCour() == "pour" || ls.getSymCour() == "lire"
                || ls.getSymCour() == "ecrire" || ls.getSymCour() == "switch");
        // tant que le symbole courant est un debut possible d'instruction...
        return si;
    }    catch (FinFichier) {
        throw;
    } catch (ErreurSymbole) {
        juste = false;
        while (ls.getSymCour() != ";") {
            if (ls.getSymCour() == "<FINDEFICHIER>") {
                throw FinFichier();
            }
            if (ls.getSymCour() == "finsi" || ls.getSymCour() == "fintantque" || ls.getSymCour() =="finpour" 
                    || ls.getSymCour() == "finswitch" || ls.getSymCour() == "fincas"){
                return new NoeudSeqInst();
            }
            ls.suivant();
        }
        ls.suivant();
        return seqInst();
        
    } catch (ErreurOperateur) {
        juste = false;
        while (ls.getSymCour() != ";") {
            if (ls.getSymCour() == "<FINDEFICHIER>") {
                throw FinFichier();
            }
            if (ls.getSymCour() == "finsi" || ls.getSymCour() == "fintantque" || ls.getSymCour() =="finpour" 
                    || ls.getSymCour() == "finswitch" || ls.getSymCour() == "fincas"){
                return new NoeudSeqInst();
            }
            ls.suivant();
        }
        ls.suivant();
        return seqInst();
        
    }


}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::inst() {
    // <inst> ::= <affectation> | <inst_condi>

    try {
        if (ls.getSymCour() == "si") return instSi();
        else if (ls.getSymCour() == "tantque") return instTq();
        else if (ls.getSymCour() == "repeter") return instRepeter();
        else if (ls.getSymCour() == "pour") return instPour();
        else if (ls.getSymCour() == "lire") return instLire();
        else if (ls.getSymCour() == "ecrire") return instEcrire();
        else if (ls.getSymCour() == "switch") return  instSwitch();
        else return affectation();
    }
    catch(exception) {
        throw;
    }

}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSi() {
    //<instSi> ::= si ( <expBool> ) <seqInst> { sinonsi ( <expBool> ) <seqInst> } [ sinon <seqInst> ] finsi

    try {
        Noeud* seq1 = NULL;
        vector<NoeudInstCond*> TabSi; //tableau avec element Instruction avec Condition en premier le Si puis tout les sinonsi
        sauterSymCour("si");
        sauterSymCour("(");
        Noeud* cond = expBool();
        sauterSymCour(")");
        Noeud* seq = seqInst();
        NoeudInstCond* Inst = new NoeudInstCond(cond, seq); //création de l'élement instruction avec condition dans le cas du si
        TabSi.push_back(Inst); //ajout de l'élement dans le vecteur

        while (ls.getSymCour() == "sinonsi") {
            sauterSymCour("sinonsi");
            sauterSymCour("(");
            cond = expBool();
            sauterSymCour(")");
            seq = seqInst();
            NoeudInstCond* Inst = new NoeudInstCond(cond, seq); //création de l'élement instruction avec condition dans le cas d'un sinonsi
            TabSi.push_back(Inst); //ajout de l'élement dans le vecteur

        }
        if (ls.getSymCour() == "sinon") {
            sauterSymCour("sinon");
            seq1 = seqInst();
        }
        sauterSymCour("finsi");
        return new NoeudInstSi(TabSi, seq1);
   
    } catch (exception) {
        throw;
    }

}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instTq() {
    //<instTq> ::= tantque ( <expBool> ) <seqInst> fintantque
    try {
        sauterSymCour("tantque");
        sauterSymCour("(");
        Noeud* cond = expBool();
        sauterSymCour(")");
        Noeud* seq = seqInst();
        sauterSymCour("fintantque");
        return new NoeudInstTq(cond, seq);
    } catch (exception) {
        throw;
    }

}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instRepeter() {
    //<instRepeter::= repeter <seqInst> jusqua ( <expBool> )
    try {
        sauterSymCour("repeter");
        sauterSymCour("(");
        Noeud* seq = seqInst();
        sauterSymCour(")");
        sauterSymCour("jusqua");
        sauterSymCour("(");
        Noeud* cond = expBool();
        sauterSymCour(")");
        return new NoeudInstRepeter(cond, seq);
    } catch (exception) {
        throw;
    }

}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instPour() {
    //<instPour::= pour (<affectation> ; <relation> ; <affectation> ) <seqInst> finpour
    try {
        sauterSymCour("pour");
        sauterSymCour("(");
        Noeud* ini = affectation();
        sauterSymCour(";");
        Noeud* cond = relation();
        sauterSymCour(";");
        Noeud* inc = affectation();
        sauterSymCour(")");
        Noeud* seq = seqInst();
        sauterSymCour("finpour");
        return new NoeudInstPour(ini, cond, inc, seq);
    }    catch (exception) {
        throw;
    }

}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instLire() {
    //instLire> ::= lire ( <variable> )  
    int val =0;
    try {
        sauterSymCour("lire");
        sauterSymCour("(");
        testerSymCour("<VARIABLE>");
        Noeud* var = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
        sauterSymCour(")");
        return new NoeudInstLire(var, val);
    }    catch (exception) {
        throw;
    }

}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instEcrire() {
    //<instEcrire> ::= ecrire ( <expression> | <chaine> )
    Noeud* exp = NULL;
    NoeudInstEcrire* nIE;
    try {
        sauterSymCour("ecrire");
        sauterSymCour("(");
        if (ls.getSymCour() == "<CHAINE>") {
            exp = ts.chercheAjoute(ls.getSymCour());
            ls.suivant();
        } else exp = expression();
        sauterSymCour(")");
        return nIE = new NoeudInstEcrire(exp);
    }    catch (exception) {
        throw;
    }



}////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSwitch() {
    //<instSwitch> ::= switch (<VARIABLE>) { cas ( <VARIABLE> ) : <seqInst> fincas; } finswitch
    vector<int> sortie;
    Noeud* var;
    NoeudInstSwitch* nIS;
    vector<NoeudInstCond*> TabCas;
    try {
        sauterSymCour("switch");
        sauterSymCour("(");
        ls.getSymCour() == "<VARIABLE>";
        var = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
        sauterSymCour(")");
        while (ls.getSymCour() == "cas") {
            sauterSymCour("cas");
            sauterSymCour("(");
            Noeud* cond = facteur();
            sauterSymCour(")");
            sauterSymCour(":");
            if (ls.getSymCour() == "stop") {
                sortie.push_back(cond->getValeur()); 
                sauterSymCour("stop");
                sauterSymCour(";");
            } else {
                Noeud* seq = seqInst();
            NoeudInstCond* Inst = new NoeudInstCond(cond, seq); //création de l'élement cas avec condition et la sequence
            TabCas.push_back(Inst); //ajout de l'élement dans le vecteur
            }
        }
        sauterSymCour("finswitch");
        return nIS = new NoeudInstSwitch(var,TabCas,sortie);
    }    catch (exception) {
        throw;
    }



}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::affectation() {
    // <affectation> ::= <variable> = <expression>

    try {
        testerSymCour("<VARIABLE>");
        Noeud* var = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
        sauterSymCour("=");
        Noeud* exp = expression();
        return new NoeudAffectation(var, exp);
    }    catch (exception) {
        throw;
    }

}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expression() {
    // <expression> ::= <terme> { <op> <terme> }
    try {
        Noeud* term = terme();
        while (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
            Symbole operateur = opAdd(); // on stocke le symbole de l'opérateur
            Noeud* termDroit = terme(); // lecture de l'operande droit
            term = new NoeudOperateurBinaire(operateur, term, termDroit); // const. du noeud
        }
        return term;
    } catch (exception) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::terme() {
    // <terme> ::= <facteur> { <opMult> facteur> }
    try {
        Noeud* fact = facteur();
        while (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
            Symbole operateur = opMult();
            Noeud* factDroit = facteur();
            fact = new NoeudOperateurBinaire(operateur, fact, factDroit);
        }
        return fact;
    } catch (exception) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::facteur() {
    // <facteur> ::= <entier>  |  <variable>  |  - <facteur>  |  ( <expression> )

    Noeud* fact = NULL;
    try {
        if (ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "<ENTIER>") {
            fact = ts.chercheAjoute(ls.getSymCour());
            ls.suivant();
        } else if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
            ls.suivant();
            // on représente le moins unaire (- facteur) par une soustractin binaire (0 - facteur)
            fact = new NoeudOperateurBinaire(Symbole("-"), ts.chercheAjoute(Symbole("0")), facteur());
        } else if (ls.getSymCour() == "(") {
            ls.suivant();
            fact = expBool();
            sauterSymCour(")");
        } else
            erreur("<facteur>");
        return fact;
    } catch (exception) {
        throw;
    }
}
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opAdd()
// <opAdd> ::= + | -
{
    try {
        Symbole operateur;
        if (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
            operateur = ls.getSymCour();
            ls.suivant();
        } else erreur("opAdd");
        return operateur;
    } catch (exception) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opMult()
// <opAdd> ::= * | /
{
    try {
        Symbole operateur;
        if (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
            operateur = ls.getSymCour();
            ls.suivant();
        } else
            erreur("<opMult>");
        return operateur;
    } catch (exception) {
        throw;
    }
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expBool()
// <expBool> ::= <expBoolEt> { <opBoolOu> | <expBoolEt> }
{
    try {
        Symbole operateur;
        Noeud* operande = expBoolEt();
        while (ls.getSymCour() == "ou") {
            operateur = opBoolOu();
            Noeud* operandeDroit = expBoolEt();
            operande = new NoeudOperateurBinaire(operateur, operande, operandeDroit);
        }
        return operande;
    } catch (exception) {
        throw;
    }
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expBoolEt()
// <expBoolEt> ::= <relation> { <opBoolEt> | <relation> }
{
    try {
        Symbole operateur;
        Noeud* operande = relation();
        while (ls.getSymCour() == "et") {
            operateur = opBoolEt();
            Noeud* operandeDroit = relation();
            operande = new NoeudOperateurBinaire(operateur, operande, operandeDroit);

        }
        return operande;
    } catch (exception) {
        throw;
    }
}
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opBoolEt()
// <opBool> ::= et | ou
{
    try {
        Symbole operateur;
        if (ls.getSymCour() == "et") {
            operateur = ls.getSymCour();
            ls.suivant();
        } else
            erreur("<opBoolEt>");
        return operateur;
    } catch (exception) {
        throw;
    }
}
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opBoolOu()
// <opBoolOu> ::= ou
{
    try {
        Symbole operateur;
        if (ls.getSymCour() == "ou") {
            operateur = ls.getSymCour();
            ls.suivant();
        } else
            erreur("<opBoolOu>");
        return operateur;
    } catch (exception) {
        throw;
    }
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::relation() {
    // <relation> ::= <expression> { <opRel> <expression> }
    try {
        Symbole operateur;
        Noeud* operande = expression();
        while (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == "<" || ls.getSymCour() == "<=" || ls.getSymCour() == ">" || ls.getSymCour() == ">=") {
            operateur = opRel();
            Noeud* operandeDroit = expression();
            operande = new NoeudOperateurBinaire(operateur, operande, operandeDroit);
        }
        return operande;
    } catch (exception) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opRel()
// <opRel> ::= == | != | < | <= | > | >=
{
    Symbole operateur;
    try {


        if (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == "<" || ls.getSymCour() == "<=" || ls.getSymCour() == ">" || ls.getSymCour() == ">=") {
            operateur = ls.getSymCour();
            ls.suivant();
        } else
            erreur("<opRel>");
        return operateur;
    } catch (exception) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opUnaire()
// <opUnaire> ::= - | non
{
    Symbole operateur;
    try {
        if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
            operateur = ls.getSymCour();
            ls.suivant();
        } else
            erreur("<opUnaire>");
        return operateur;
    } catch (exception) {
        throw;


    }
}
////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::testerSymCour(string ch) {

    if (ls.getSymCour() != ch) {
        cout << "Erreur de Symbole" << endl;
        cout << endl << "-------- Erreur ligne " << ls.getLigne()
                << " - Colonne " << ls.getColonne() << endl << "   Attendu : "
                << ch << endl << "   Trouve  : " << ls.getSymCour() << endl
                << endl;
        throw ErreurSymbole();
    }


}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::sauterSymCour(string ch) {

    testerSymCour(ch);
    ls.suivant();


}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::erreur(string mess) {
    cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
            << ls.getColonne() << endl << "   Attendu : " << mess << endl
            << "   Trouve  : " << ls.getSymCour() << endl << endl;
    throw ErreurOperateur();
}
